﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections;
using Microsoft.JScript;
using Microsoft.JScript.Vsa;

namespace Oscuridad
{
    public class dados
    {
        ArrayList tiradas = new ArrayList();
        
        public dados()
        {

        }

        public double[] Lanzar()
        {
            if (tiradas.Count == 0) return new double[] {-1};

            tirada[] ts = (tirada[])tiradas.ToArray(typeof(tirada));
            return Lanzar(ts);
        }

        public double Lanzar(tirada Tirada)
        {
            return Tirada.Resolver();
        }

        public double[] Lanzar(tirada[] tiradas)
        {
            double[] result = new double[tiradas.Length];

            for (int i = 0; i <= tiradas.Length-1; i++)
            {
                tirada t = tiradas[i];
                result[i] = t.Resolver();
            }

            return result;
        }

        public double Lanzar(string expresion)
        {
            tirada t = new tirada(expresion);
            return t.Resolver();
        }

        public double[] Lanzar(params string[] expresiones)
        {
            double[] result = new double[expresiones.Length];

            for (int i = 0; i <= expresiones.Length - 1; i++)
            {
                tirada t = new tirada(expresiones[i]);
                result[i] = t.Resolver();
            }

            return result;
        }

        public void Add(tirada Tirada)
        {
            tiradas.Add(Tirada);
        }

        public void Clear()
        {
            tiradas.Clear();
        }
    }

    public class tirada
    {
        protected enum eDados
        {
            D4 = 4,
            D6 = 6,
            D8 = 8,
            D10 = 10,
            D12 = 12,
            D20 = 20,
            D30 = 30,
            D100 = 100
        }

        public string expresion;
        private string[] patterns = { "[\\d]*(dado)\\[[\\d]*(-)[\\d]*\\]", "[\\d]*(dado)" };

        public tirada(string Expresion)
        {
            expresion = Expresion;
        }

        internal double Resolver()
        {
            //si no viene expresión, devolver -1
            if (expresion == "") return -1;

            //iniciliazar el random
            Random random = new Random();

            //guardar la expresión en una cadena temporal
            string expaux = expresion;
            
            //cambiar los valores de cadena de los dados (D10, D20, D6, etc...) a valores numéricos
            //recorremos los valores del enum al reves, para cambiar antes los D100 que los D10
            int[] valores = (int[]) Enum.GetValues(typeof(eDados));
            for (int i = valores.Length - 1; i >= 0; i--)
            {
                //dado a buscar
                string d = "D" + valores[i].ToString();
                foreach (string pattern in patterns) 
                {
                    //expresión regular para encontrar los dados necesarios del valor indicado
                    string reg = pattern.Replace("dado", d);
                    Regex rx = new Regex(reg);
                    MatchCollection matches = rx.Matches(expaux);
                    foreach (Match match in matches)
                    {
                        string exp = match.Value;
                    
                        //si el dado encontrado no tiene un número que le preceda, la cantidad de dados a tirar es 1
                        int cantidad = 1;
                        int min = 1;
                        int max = valores[i];
                        if (exp.IndexOf("D") > 0)
                        {
                            cantidad = int.Parse(exp.Split('D')[0]);
                            max = valores[i] * cantidad;
                        }
                        if (exp.IndexOf("[") > 0 && exp.IndexOf("]") > 0)
                        {
                            string limites = exp.Substring(exp.IndexOf("[") + 1, exp.IndexOf("]") - exp.IndexOf("[") - 1);
                            min = int.Parse(limites.Split('-')[0]);
                            max = int.Parse(limites.Split('-')[1]);
                        }

                        eDados dado = (eDados)valores[i];
                        expaux = expaux.Replace(exp, ValorRandom(random, cantidad, min, max, valores[i]).ToString());
                    }
                }
            }

            //evaluar expresión
            //el VSAengine está "deprecated", pero es la única forma (por triste que parezca) de evaluar una expresión de cadena en c# sin referencias externas.
            VsaEngine engine = VsaEngine.CreateEngine();
            object o = null;
            double result = 0;
            try
            {
                o = Microsoft.JScript.Eval.JScriptEvaluate(expaux, engine);
                result = System.Convert.ToDouble(o);
            }
            catch
            {
                result = 0;
            }

            return result;
        }

        private int ValorRandom(Random random, int cantidad, int min, int max, int limite)
        {
            int result;
            
            do {
                result = 0;
                for (int i = 1; i <= cantidad; i++)
                    result += random.Next(0, limite) + 1;
            } while (result < min || result > max);

            return result;
        }
    }

}
